﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Markup;
using System.Security.Principal;

namespace UltimateNavigationFramework.Authorisation
{
    [ContentProperty("Rules")]
    public class NavigationAuthoriser : DependencyObject, INavigationAuthoriser
    {
        /// <summary>
        /// The set of rules used by the authoriser.
        /// </summary>
        public static readonly DependencyProperty RulesProperty =
            DependencyProperty.Register("Rules",
                                        typeof(DependencyObjectCollection<AuthorisationRule>),
                                        typeof(NavigationAuthoriser),
                                        new PropertyMetadata(null));

        /// <summary>
        /// The invalid access page uri to redirect to should the authorisation fail due to insuffient user rights
        /// </summary>
        public static readonly DependencyProperty ErrorUriProperty =
            DependencyProperty.Register("ErrorUri",
                                typeof(String),
                                typeof(NavigationAuthoriser),
                                new PropertyMetadata(null));

        /// <summary>
        /// The principal that will be used to check authorization.
        /// </summary>
        public static readonly DependencyProperty PrincipalProperty =
            DependencyProperty.Register("Principal",
                                        typeof(IPrincipal),
                                        typeof(NavigationAuthoriser),
                                        new PropertyMetadata(null));

        /// <summary>
        /// Constructs a NavigationAuthorizer.
        /// </summary>
        public NavigationAuthoriser()
        {
            this.Rules = new DependencyObjectCollection<AuthorisationRule>();
        }

        /// <summary>
        /// The set of rules used by the authoriser.
        /// </summary>
        public DependencyObjectCollection<AuthorisationRule> Rules
        {
            get { return (DependencyObjectCollection<AuthorisationRule>)this.GetValue(RulesProperty); }
            set { this.SetValue(RulesProperty, value); }
        }

        /// <summary>
        /// The error uri used by the authoriser.
        /// </summary>
        public String ErrorUri
        {
            get { return this.GetValue(ErrorUriProperty).ToString(); }
            set { this.SetValue(ErrorUriProperty, value); }
        }

        /// <summary>
        /// The principal that will be used to check authorization.  Bind this to, for example, the
        /// </summary>
        public IPrincipal Principal
        {
            get { return (IPrincipal)this.GetValue(PrincipalProperty); }
            set { this.SetValue(PrincipalProperty, value); }
        }

        #region INavigationAuthoriser Implementation
        /// <summary>
        /// Checks whether the principal has sufficient authorisation to access the Uri.
        /// If the principal is authorized, this method should simply return.  Otherwise, it should throw.
        /// </summary>        
        /// <param name = "targetUri">The Uri being loaded.</param>        
        public void CheckAuthorization(Uri targetUri)
        {
            foreach (var rule in this.Rules.Where(rule => rule.Matches(targetUri)))
            {
                rule.Check(this.Principal);
                return;
            }
        }
        #endregion
    }
}
